/*
    SEL - Simple DirectMedia Layer Extension Library
    Copyright (C) 2001 Matej Knopp <knopp@users.sf.net>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifndef __sel_mutex_h__
#define __sel_mutex_h__

#include "sdl.h"

namespace SEL
{

//! The Mutex class protects variables that are accessed from multiple
//! threads.
/*!
  Mutex is a thread synchronisation primitive and it's purpose is to
  protect variables from being accessed from multiple threads at the
  same time.
  \par Example:
  \code
  int glob; // global variable
  SEL::Mutex glob_mutex; // create an unlocked mutex
  
  //this funtion may be called from different threads
  void access_glob () 
  {
  	glob_mutex.lock ();
	cout << ++glob << endl;
	glob_mutex.unlock ();
  }
  \endcode
 */
class Mutex
{
public:
	//! Create a new unlocked mutex.
	/*!
	  On any error throws an exception.
	 */
	Mutex ();
	
	//! Destroy the mutex.
	~Mutex ();
	
	//! Lock the mutex.
	/*!
	  If a thread tries to lock a locked mutex. the thread will 
	  wait until the mutex gets unlocked, so after locking you
	  should unlock the mutex as soon as possible because if not,
	  it may lead to a performance decline.
	  \return true, or false on error.
	 */
	bool lock ();
	
	//! Unlock the mutex.
	/*!
	  This allows access to protected variable.
	  \return true, or false on error.
	  */
	bool unlock ();			
	
	//! Return the wrapped SDL::mutex structure.
	SDL::mutex *mutex () 			{ return m_mutex; }

	//! Return the wrapped constant SDL::mutex structure.
	const SDL::mutex *mutex () const	{ return m_mutex; }

protected:
	//! The wrapped SDL::mutex structure.
	SDL::mutex *m_mutex;
};

//! The semaphore class provides a semaphore thread synchronisation
//! primitive.
/*!
  Each semaphore has a value associated with it. The run of a synchronisated
  thread depends on the value.
 */
class Semaphore
{
public:
	//! Create a new semaphore initialized with \a initial_value.
	/*!
	  If the thread can't be created, it will throw an exception.
	  \param initial_value the initial value assigned to the semaphore.
	 */
	Semaphore (Uint32 initial_value);
	
	//! Destroy the semaphore.
	~Semaphore ();
	
	//! Suspend the calling thread until the semaphore has a positive
	//! count.
	/*!
	  It then atomically decreases the semaphore's count.
	  \return true, or false on error.
	 */
	bool wait ();
	
	//! Non-blocking variant of Semaphore::wait ().
	/*!
	  \return 0 if the wait succeeds, SDL::MUTEX_TIMEDOUT if the wait
	  would block, and -1 on error.
         */
	int tryWait ();

	//! Variant of Semaphore::wait () with a timeout in milliseconds.
	/*!
	  \return 0 if the wait succeeds, SDL::MUTEX_TIMEDOUT if the wait
	  does not succeed in the allotted time, and -1 on error.
	  \warning On some platforms this function is implemented by
	  looping with a delay of 1 ms, and so should be avoided if possible.
	  \param ms the timeout in milliseconds.
         */
	int waitTimeout (Uint32 ms);
	
	//! Atomically increase the semaphore's count (not blocking)
	/*!
	  \return true, or false on error.
	 */
	bool post ();
	
	//! Return the current count of the semaphore.
	Uint32 value () const;
	
	//! Return the wrapped SDL::sem structure.
	SDL::sem *semaphore () 			{ return m_sem; }

	//! Return the wrapped constant SDL::sem structure.
	const SDL::sem *semaphore () const	{ return m_sem; }

protected:
	//! The wrapped SDL::sem structure.
	SDL::sem *m_sem;
};

//! The condition variable class.
class Cond
{
public:
	//! Create a new condition variable.
	/*!
	  On any error it throws an exception.
	 */
	Cond ();
	
	//! Destroy the condition variable.
	~Cond ();
	
	//! Restart one of the threads that are waiting on the condition
	//! variable.
	/*!
	  \return true, or false on error.
         */
	bool signal ();
	
	//! Restart all threads that are waiting on the condition variable
	/*!
	  \return true, or false on error.
	 */
	bool broadcast ();
	
	//! Wait on the condition variable, unlocking the provided mutex.
	/*!
	  \warning The mutex must be locked before entering this function!
	  \return true, or false on error.
	 */
	bool wait (SEL::Mutex & mutex);
	
	//! Wait for at most \a ms milliseconds.
	/*!
	  \return 0 if the condition variable is signaled, SDL::MUTEX_TIMEDOUT
	  if the condition is not signaled in the alloted time, and -1 on error.
	  \warning On some platforms this function is implemented by
	  looping with a delay of 1 ms, and so should be avoided if possible.
	  \sa wait (SEL::Mutex &mutex)
	 */
	int waitTimeout (SEL::Mutex & mutex, Uint32 ms);
	
	//! Return the wrapped SDL::cond structure.
	SDL::cond *cond () 			{ return m_cond; }

	//! Return the wrapped constant SDL::cond structure.
	const SDL::cond *cond () const		{ return m_cond; }

protected:
	//! The wrapped SDL::cond structure.
	SDL::cond *m_cond;
};

//////////////////////////////////////////////////////////////
/////////////////////  INLINE FUNCTIONS  /////////////////////
//////////////////////////////////////////////////////////////

inline bool
Mutex::lock ()
{
	return SDL::LockMutex (m_mutex) == 0;
}

inline bool
Mutex::unlock ()
{
	return SDL::UnlockMutex (m_mutex) == 0;
}

////////////////////////////////////////////////////////////
// SEMAPHORE ///////////////////////////////////////////////
////////////////////////////////////////////////////////////

inline bool 
Semaphore::wait ()
{
	return SDL::SemWait (m_sem);
}
	
inline int
Semaphore::tryWait ()
{
	return SDL::SemTryWait (m_sem) == 0;
}

inline int 
Semaphore::waitTimeout (Uint32 ms)
{
	return SDL::SemWaitTimeout (m_sem, ms);
}
	
inline bool 
Semaphore::post ()
{
	return SDL::SemPost (m_sem) == 0;
}
	
inline Uint32 
Semaphore::value () const
{
	return SDL::SemValue (m_sem);
}

////////////////////////////////////////////////////////////
// CONDITION VARIABLE //////////////////////////////////////
////////////////////////////////////////////////////////////

inline bool 
Cond::signal ()
{
	return SDL::CondSignal (m_cond) == 0;
}
	
inline bool
Cond::broadcast ()
{
	return SDL::CondBroadcast (m_cond) == 0;
}
	
inline bool
Cond::wait (SEL::Mutex & mutex)
{
	return SDL::CondWait (m_cond, mutex.mutex ()) == 0;
}
	
inline int
Cond::waitTimeout (SEL::Mutex & mutex, Uint32 ms)
{
	return SDL::CondWaitTimeout (m_cond, mutex.mutex (), ms);
}

} // namespace SEL

#endif // __sel_mutex_h__
